import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;


public class Board extends ArrayList<Node> {
	final static int MAX_ROW = 30;
	final static int MAX_COL = 30;
	final static int MIN_ROW = 1;
	final static int MIN_COL = 1;
	static int numRows, numCols;
	static public Node iniNode, endNode;
	
	NodeList visitados = new NodeList();
	NodeList obstaculos = new NodeList();
	Stack<Node> nodeStack = new Stack<Node>();
	
	Board() {}
	
	Board(int r, int c) {
		dimensionar(r, c);
	}
	
	// crea tabla dimensiones r x c
	void dimensionar(int r, int c) {
		numRows = r;
		numCols = c;
	
		int x = 0;
		int y = 0;
		for (int i = 0; i < numRows * numCols; i++) {
			this.add(new Node(x, y));
			y = (y + 1) % numCols;
			if (y == 0)
				y++;
		}
	}
	
	// coloca n como nodo inicial
	void setIniNode(Node n) {
		iniNode = n;
	}
	
	// coloca n como nodo final
	void setEndNode(Node n) {
		endNode = n;
	}

	// evalua si la posicion (x, y) esta dentro del tablero
	static boolean inside(int x, int y) {
		System.out.print(x+y);
		return (0 <= x && x < numRows && 0 <= y && y < numCols);
	}
	
	boolean setNode(int x, int y, Node c) {
		int pos = position(x, y);
		if ((pos != -1) && this.get(position(x,y)).type == Node.FREE) {
			this.add(pos, c);
			this.remove(pos + 1);
			
			if (c.equals(Node.OBST)) {
				System.out.println("--- Obstaculo en (" + x + ", " + y + "), position_lista = " + position(x,y));
			}			
			return true;
		}
		System.out.println("peta 1");
		return false;
	}
	
	// devuelve el nodo en la posicion (x, y)
	Node get(int x, int y) {
		return this.get(position(x, y));	
	}
	
	
	static int position(int x, int y) {
		if ((MIN_ROW <= x + 1) && (x + 1 <= numRows) && (MIN_COL <= y + 1) && (y + 1 <= numCols)) {
			return x * numCols + y;
		}
		else {
			System.err.println("*** Se ha salido de la tabla en (" + x + ", " + y + ")");
			return -1;
		}
	}
	
	void setType(Node n, int i) {
		int pos = position(n.coordX, n.coordY);
	
		n.setType(Node.VIS);
		this.remove(pos);
		this.add(pos, n);
		System.err.print("SWAP: " + get(pos) + (get(pos).toStringNum()));
		//System.out.println(this);

	}
	
	Node setVisit(Node n, int i) {
		int pos = position(n.coordX, n.coordY);
		Node n2 = new Node(n.coordX, n.coordY);
		n2.setType(i);
		this.remove(pos);
		this.add(pos, n2);
		return n2;
	}
	
	public String toString() {
		String str = 
				"INI: " + iniNode.toStringNum() + 
				"FIN: " + endNode.toStringNum();
		for (int i = 0; i < numRows * numCols; i++) {
			str += this.get(i) + " ";
			if ((i + 1) % numCols == 0) {
				str += "\n";
			}
		}
		return str;		
	}
	
	void crearObstaculo(int x, int y) {
		Node c = new Node(x, y);
        c.setType(Node.OBST);
        obstaculos.add(c);
	}
	
	// creo k obstaculos aleatoriamente
	boolean setRandomObstacle(int k) {
		System.out.println("--- Creamos " + k + " obstaculo aleatoriamente");
	    Random randomGenerator = new Random();
	    int i = 0;
	    while (i < k) {
			int xRand = randomGenerator.nextInt(Board.numRows);
			int yRand = randomGenerator.nextInt(Board.numCols);
			
			Node n = new Node(xRand, yRand);
			if (!(xRand == 0 && yRand == 0) && !(xRand == Board.numRows - 1 && yRand == Board.numCols - 1)) {
				crearObstaculo(xRand, yRand);
				i++;
			}
		}
	    return true;
	}
	
	
	
	// carga tablero desde fichero
	void loadFromFile(String filename) throws IOException {
		System.out.println(filename);
		try {
			BufferedReader in = new BufferedReader(new FileReader("fileIn/" + filename));
	
			String s;
	
			int row = -1;
			int col = -1;
			int randObst = 0;
			int [] ini = {-1, -1};
			int [] end = {-1, -1};
			
			ArrayList<Integer> xObst = new ArrayList<Integer>();
			ArrayList<Integer> yObst = new ArrayList<Integer>();
			
			while ((s = in.readLine()) != null) {
				String [] array = s.split(":");
				if (array[0].equals("row"))
					row = Integer.parseInt(array[1]);
				if (array[0].equals("col")) 
					col = Integer.parseInt(array[1]);
				if (array[0].equals("randObst")) 
					randObst = Integer.parseInt(array[1]);
				if (array[0].equals("obst")) {
					xObst.add(Integer.parseInt(array[1]));
					yObst.add(Integer.parseInt(array[2]));
				}
				if (array[0].equals("ini")) {
					ini[0] = Integer.parseInt(array[1]);
					ini[1] = Integer.parseInt(array[2]);
				}
				if (array[0].equals("end")) {
					end[0] = Integer.parseInt(array[1]);
					end[1] = Integer.parseInt(array[2]);
				}
			}
			
			dimensionar(row, col);
			
			for (int i = 0; i < xObst.size(); i++) {
				Node c = new Node(xObst.get(i), yObst.get(i));
		        c.setType(Node.OBST);
		        obstaculos.add(c);
			}
	
			setRandomObstacle(randObst);
		
			this.setIniNode(new Node(ini[0], ini[1]));
			this.setEndNode(new Node(end[0], end[1]));
		
			//System.out.println(table_1);
			
			in.close();
		} catch (IOException ioe) {
			System.out.println("Error al abrir el fichero");
		}
	}
	
	void saveInFile(String filename) {
		// creamos string con la salida
		String source = 
				"row:" + Board.numRows + "\n" +
				"col:" + Board.numCols + "\n" +
				"ini:" + Board.iniNode.coordX + ":" + Board.iniNode.coordY + "\n" +
				"end:" + Board.endNode.coordX + ":" + Board.endNode.coordY + "\n";
	
		for (int i = 0; i < this.obstaculos.size(); i++) {
			Node nodeAux = this.obstaculos.get(i);
			source += "obst:" + nodeAux.coordX + ":" + nodeAux.coordY + "\n";
		}		
		try {
			FileWriter f0 = new FileWriter("fileIn/" + filename);
			f0.write(source);
			f0.close();
			System.out.print("Fichero creado" + filename);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/*********************************************************************
	 *  RECORRIDOS
	 ********************************************************************/
	// Recorrido Busqueda a ciegas
	public	boolean blindSearch(Node n) {
		if (n == null ) {
			System.out.println("null");
			return false;
		}
		if (visitados.contains(n)) {
			System.out.println(n.toStringNum() + "ya esta visitado");
			return false;
		}
		if (obstaculos.contains(n)) {
			System.out.println(n.toStringNum() + "es obstaculo");
			return false;
		}
		if (Board.iniNode.equals(n)) {
			return true;
		}
		
		visitados.add(n);
		nodeStack.push(n);
		
		boolean flag2 = (blindSearch(n.west()) || blindSearch(n.north()) || blindSearch(n.east()) || blindSearch(n.south()));
		if (!flag2 && !nodeStack.empty())
			nodeStack.pop();
		return flag2;
	}
	
	// Recorrido Busqueda Escaladora (sirve para varios tipos de distancias, parametro [int distID])
    public boolean climbingSearch(Node n, int distID) {
		if (n.isNull()) {
			System.out.println("null");
			return false;
		}
		if (visitados.contains(n)) {
			System.out.println(n.toStringNum() + "ya esta visitado");
			return false;
		}
		if (obstaculos.contains(n)) {
			System.out.println(n.toStringNum() + "es obstaculo");
			return false;
		}
		
		visitados.add(n);
		nodeStack.push(n);		
		
		if (iniNode.equals(n)) {
			System.out.println("TROVATO");
			return true;
		}
	
		boolean flag2 = 
				(n.west().distance(iniNode, distID) <= n.distance(iniNode, distID) && climbingSearch(n.west(), distID)) ||
				(n.north().distance(iniNode, distID) <= n.distance(iniNode, distID) && climbingSearch(n.north(), distID)) ||
				(n.east().distance(iniNode, distID) <= n.distance(iniNode, distID) && climbingSearch(n.east(), distID)) ||
				(n.south().distance(iniNode, distID) <= n.distance(iniNode, distID) && climbingSearch(n.south(), distID));
		if (!flag2 && !nodeStack.empty()) {
			visitados.remove(nodeStack.pop());
		}
		return flag2;
	}
	
}
